/**
 * @file types.h
 * @brief Various primitive types used by filter.
 */

#pragma once

#include <cstdint>
#include <string>
#include <vector>
#include <optional>
#include <unordered_map>

#include "core/packet.h"

/// @brief Filter action enumeration.
enum class FilterAction { ACCEPT, DROP, REJECT, LOG };

/// @brief Range of IP address.(CIDR)
struct IPRange {
    uint32_t base;
    uint32_t mask;
    uint8_t prefix_length;

    /// @brief Create from CIDR string.
    static std::optional<IPRange> fromCIDR(const std::string& cidr);

    /// @brief Check if an IP address is within the range.
    bool contains(uint32_t ip) const;
};

/// @brief Range of port.
struct PortRange {
    uint16_t start;
    uint16_t end;

    /// @brief Check if a port is within the range.
    bool contains(uint16_t port) const {
        return port >= start && port <= end;
    }
};

struct BasicCondition {
    std::optional<IPRange> src_ip;
    std::optional<IPRange> dst_ip;
    std::optional<PortRange> src_port;
    std::optional<PortRange> dst_port;
    std::optional<uint8_t> protocol;

    /// @brief Check if a packet matches.
    bool matches(const Packet& packet) const;
};

struct FilterRule {
    uint32_t id;
    std::string name;
    BasicCondition condition;
    FilterAction action;
    uint32_t priority;
    uint64_t hit_count = 0;
    bool enable = true;

    /// @brief Check if packet match the condition.
    bool match(const Packet& packet) const {
        return enable && condition.matches(packet);
    }
};

struct FilterResult {
    FilterAction action;
    uint32_t matched_rule_id;
    std::string matched_rule_name;
    bool packet_allowed;

    FilterResult(FilterAction act = FilterAction::ACCEPT,
                 uint32_t rule_id = 0,
                 const std::string& rule_name = "")
        : action(act), matched_rule_id(rule_id), matched_rule_name(rule_name), packet_allowed(act == FilterAction::ACCEPT){}
};

/// @brief Filter statistics.
struct FilterStats {
    uint64_t total_packets = 0;
    uint64_t accepted_packets = 0;
    uint64_t dropped_packets = 0;
    uint64_t filtered_packets = 0;

    std::unordered_map<uint32_t, uint64_t> rule_hit_counts;

    void reset() {
        total_packets = 0;
        accepted_packets = 0;
        dropped_packets = 0;
        filtered_packets = 0;
        rule_hit_counts.clear();
    }
};